Whilst base R plots are quick and useful for examining
our data, they don’t always offer the flexibility and attractive
customization options that we’d like for a presentation or manuscript.
This is where ggplot2 comes in.
This session will teach you the basics of using ggplot2
to visualize data in R. ggplot was developed in 2005 by Hadley Wickham
as an open source data visualization package for R. With
ggplot2, you can create plots that range from simple
scatter diagrams to complex custom plots that are (almost) completely
customizable.
Resources:
https://towardsdatascience.com/guide-to-data-visualization-with-ggplot2-in-a-hour-634c7e3bc9dd
https://ggplot2.tidyverse.org/reference/
https://www.youtube.com/@Riffomonas
Install necessary packages
ggplot2 is included in the tidyverse
package, so we can simply install and load tidyverse for
everything we’ll cover in this session.
packages_to_install <- c("tidyverse","ggtext")
for (package in packages_to_install) {
if (!(package %in% rownames(installed.packages()))) {
install.packages(package, dependencies = TRUE)
print(paste("Installed package:", package))
} else {
print(paste(package, "is already installed"))
}
}
[1] "tidyverse is already installed"
[1] "ggtext is already installed"
library(tidyverse)
library(ggtext)
Understanding ggplot syntax
There are three fundamental elements that go into constructing a plot
with ggplot2:
Data - dataframe to be plotted
data = dataframe
Aesthetics - maps variables to elements of the plot
(i.e. x axis, y axis, color scheme, etc.)
mapping = aes()
Geometry/Layers - visual elements used for the data
+ geom_function()
The typical input code for ggplot will usually look something like
this:
ggplot(data = df, # input data
mapping = aes(x = var1, # input mapping aesthetics
y = var2,
color = var3)) +
geom_point() # add plotting layer
Setting up data for success
One of the most important parts of getting ready to plot data in
R is ensuring that your data are “tidy”. When passing
instructions to ggplot2, the program interprets dataframes
in a fixed way:
columns are variables
rows are observations
Let’s examine a dataframe to better understand how
ggplot2 interprets data. Here, we will be using the Iris
dataset. The Iris dataset is built-in to R and was
introduced by British statistician and biologist Ronald A. Fisher in
1936. Fisher collected the data to study the variation in iris flowers
of three different species: Iris setosa, Iris versicolor, and Iris
virginica.
head(iris) # view the first six rows of the dataframe
Looking at the first rows of this dataframe we can see that each
variable is contained in a column and each row is an
observation. This means that if you have replicate
measurements (as in this dataset, there are multiple measurements of
each variable per species) you will need to have a row per
replicate rather than storing the replicate data in columns.
Other important notes about this dataset:
it consists of four numeric variables (Sepal.Length, Sepal.Width,
Petal.Length, Petal.Width) and one categorical variable (Species). This
structured format makes it easy to map variables to aesthetics in
ggplot2.
the Iris dataset has a balanced class distribution. Each of the
three species (setosa, versicolor, virginica) has an equal number of
observations. This balance allows for fair visual comparisons and avoids
potential biases that can arise from imbalanced datasets.
column names and features contain no spaces or “-”.
R doesn’t usually like these.
Let’s build a plot!
ggplot2 builds plots in layers. You can start with a
layer showing raw data, then continue to add up additional elements to
produce your desired graph. This approach will help you reduce the gap
between the expected outcomes in your head and the plots in reality.
ggplot(data = iris, # use 'data' argument to tell ggplot which dataframe we want to plot from
mapping = aes(x = Petal.Length, # mapping determines which variables are assigned to plot elements
y = Petal.Width)) -> basicPlot
basicPlot

We have told ggplot2 which variables we want to plot on
the x and y axes but we have not told ggplot2 which
geometric elements (i.e. geoms) to use to construct the plot, so all we
have are the axes and a blank plot.
What are geoms?
Geoms are the geometric objects (e.g. lines, bars, etc.) that
determine how observations are rendered. Layering elements in a plot
usually starts with adding geoms. Let’s add geom_point() to
our basic plot to create a scatter plot:
ggplot(data = iris,
aes(x = Petal.Length, # it is very common to see 'mapping =' omitted from the code - ggplot will accept either
y = Petal.Width)) + # use a + to add elements to your plot
geom_point() -> basicScatter
basicScatter

Another way you can add layers to a plot is by simply adding them to
the end of the object that we assigned our first plot to. It is very
common to see this in online guides and forums (such as Stack Overflow)
where you might look for help with R coding:
basicPlot +
geom_point()

Although this generates the same output, I would generally avoid
making your plots this way - if you end up with something that isn’t
quite working as expected I find it can be easier to fix if all your
code is laid out in front of you, rather than having to revisit each
individual step in the process of making your plot.
Now we’ll start to add some more elements to our mapping aesthetics
to better illustrate our data.
IMPORTANT NOTE
The initial mapping that you specify in the ggplot2
command (i.e. axes, color, size, etc.) are by default used globally for
the plot and are carried over to any geoms you add in the following
code. Each geom can have it’s own separate mapping aesthetics,
which can allow you to create more complex plots. If you ever run into
issues where a geom is not behaving as you would expect, take a look
back through your code and check where your aesthetics were assigned,
and how they apply to the geoms you are trying to layer.
Let’s color our points by Species:
ggplot(data=iris,
mapping = aes(x=Petal.Length,
y=Petal.Width,
color = Species)) + # tell ggplot that color is determined by Species variable
geom_point() -> colorScatter
colorScatter

Now we can add a simple regression using geom_smooth()
and we can demonstrate how changing global vs. specific aesthetics
affect geoms. Some geoms have specialized arguments that allow them to
function. In this case, geom_smooth() allows us to tell it
which method to use to generate the curve that it will plot. We will opt
for lm which is a linear model.
ggplot(data=iris,
mapping = aes(x=Petal.Length,
y=Petal.Width,
color = Species)) +
geom_point() +
geom_smooth(method = lm) -> colorScatterlm
colorScatterlm

This creates three separate curves that map to the points from each
Species by color, as this is what is specified in the global aesthetics.
Let’s change that and plot a curve that spans all points:
ggplot(data=iris,
mapping = aes(x=Petal.Length,
y=Petal.Width)) + # remove color from global aesthetics
geom_point(aes(color = Species)) + # set geom_point aesthetics - this will only color points
geom_smooth(method = lm) -> colorScatterlm2
colorScatterlm2

Now we can see that the points are still colored by Species, but the
regression is not.
Let’s play with some more aesthetics:
ggplot(data=iris,
mapping = aes(x=Petal.Length,
y=Petal.Width)) +
geom_point(aes(color = "blue")) +
geom_smooth(method = lm) -> basicPlotBlue
basicPlotBlue

Notice how even though we have changed the aesthetic of the points to
be “blue”, it has not made them blue. If we want to make all the points
one color (or a different shape, or a different size) these are
not set by aesthetics, as they are not dependent on a
variable.
Let’s make our points blue and change their shape:
ggplot(data = iris,
mapping = aes(x = Petal.Length,
y = Petal.Width)) +
geom_point(color = "blue", # note that these options are not parsed through the aes() argument
size = 5,
shape = 1) -> openCircleBlue
openCircleBlue

# shapes are defined by a numerical value
# available shapes can be viewed at https://www.datanovia.com/en/blog/ggplot-point-shapes-best-tips/ or by using ggpubr::show_point_shapes()
There are other ways we can control how each layer is rendered. Let’s
start with controlling “scales”. Scales allow us to edit specific
elements of the aesthetics and are named in a uniform manner than
describes how they act and what they affect. The names are made up of
three pieces separated by “_”:
scale
- the name of the aesthetic (e.g.
color,
shape, size, etc.)
- the name of the scale (e.g.
manual,
continuous, discrete, etc.)
Within the scales there are several options we can edit:
name = this controls what the variable is called within
the plot or legend
values = this allows us to manually input the variables
(i.e. colors or shapes) used in the plot
labels = this controls the data labels (i.e. species
names) in the plot or legend
Let’s use scale_color_manual to manually select some
colors for our plot.
(There are set named colors that can be used in R https://stat.columbia.edu/~tzheng/files/Rcolor.pdf but
you can also use hex codes. Make sure to use color blind friendly color
schemes for figures you plan to present or publish!)
ggplot(data = iris,
mapping = aes(x = Petal.Length,
y = Petal.Width,
color = Species)) +
geom_point() +
geom_smooth(method = lm) +
scale_color_manual(name = "Iris species",
values = c("setosa" = "pink",
"versicolor" = "plum",
"virginica"="seagreen3"),
labels = c("Iris setosa",
"Iris versicolor",
"Iris virginica")) -> multiColor
multiColor

Now we have some different colors and data labels in our plot of the
Iris data. The name and labels options in the
scale are useful for changing how the data are labeled in
your plot without needing to manipulate the raw data.
We can also use continuous color scales to visually represent changes
in values:
ggplot(data = iris,
mapping = aes(x = Petal.Length,
y = Petal.Width)) +
geom_point(aes(color = Petal.Length)) +
geom_smooth(method = lm) -> blueCont
blueCont

You can also use other variables within the dataframe to control the
aesthetics of the plot.
ggplot(data = iris,
mapping = aes(x = Petal.Length,
y = Petal.Width)) +
geom_point(aes(color = Sepal.Length, # color is dependent on sepal length
size = Sepal.Width)) + # point size is dependent on sepal width
geom_smooth(method = lm,
color = "black",
se = FALSE) + # change the color of the curve
scale_color_gradient(high = "purple",
low = "orange") # manually set the colors of the gradient scale

Obviously there is a little too much data now contained in this plot
for it to be particularly useful, but it is a good example of how much
data you can display and the different ways you can present it using
R.
Let’s tidy up our plot and make something that looks a little more
“publication-ready”:
First, let’s assign our chosen color scale to a vector object so we
can call the same colors for any future plots without needing to write
out the code every time. This time I’m going to use a color blind
friendly palette generated using this tool: https://davidmathlogic.com/colorblind.
plotColors <- c("setosa" = "#648FFF",
"versicolor" = "#DC267F",
"virginica"="#FFB000")
ggplot(data = iris,
mapping = aes(x = Petal.Length,
y = Petal.Width)) +
geom_point(aes(color = Species)) +
geom_smooth(method = lm,
color = "black") +
theme_bw() + # this is a built-in theme that removes the gray plot background
scale_color_manual(values = plotColors) + # direct ggplot to our color vector
ylab("Petal width (mm)") + # change y axis label - can also be done with scales
labs(x = "Petal length (mm)",
color = "Iris species") + # change legend title - can also be done with scales as previously
ggtitle("Petal width by petal length per species") + # add plot title
theme(plot.title = element_text(hjust = 0.5)) -> multiColorTidy # center plot title
multiColorTidy

Perfect! Now we can write our plot to a pdf:
pdf("multi.color.iris.plot.lm.pdf", # file name to write to
height = 4, # plot height in inches
width = 6) # plot width in inches
multiColorTidy # tell R which plot to write to file
dev.off() # this tells R that you're done creating a file
null device
1
Or we can use ggsave(), which is a function of
ggplot2 to save as any other graphics file type:
ggsave(plot = multiColorTidy, # specify plot
"multi.color.iris.plot.lm.tiff", # specify file name
height = 4, # plot height
width = 6, # plot width
units = c("in"), # specify which units to use for height and width
device = "tiff") # specify file type for saving - ggsave will also guess depending on the extension used in file name
Plotting group means and error bars
Another way that we may want to plot our data is by plotting both
group means and individual data points. This can help people
better visualize the spread of our data. This is easy enough with geoms
like geom_boxplot() and geom_violin() that
have group metrics built into their functionality.
ggplot(data = iris,
aes(x = Species,
y = Petal.Length,
fill = Species)) +
geom_boxplot(outliers = FALSE) +
geom_point()

You can see that geom_boxplot has automatically
generated a box that displays the median (thick line) and box that spans
the 25th - 75th percentiles, with whiskers that extend to the furthest
value no more than 1.5 X the IQR from the box. Values beyond the
whiskers would be counted as outliers and plotted separately. This is
great for taking a quick summary look at your data.
But what if we want to use something like geom_bar()
that does not have built in group functionality?
There are a couple of ways we could solve this issue using functions
in the tidyverse package. First, we could create a summary
table that contains grouped information for our data using
summarise.
iris %>%
group_by(Species) %>% # group_by tells R which variable to use to group observations
summarise(mean.Petal.Length = mean(Petal.Length), # add a column containing mean values per species
standard.deviation = sd(Petal.Length)) -> irisSummary # add a column containing standard deviation
head(irisSummary)
We can use the summarise function to create a new
dataframe that contains a mean and standard deviation for each species.
We can write this to a new object and then use this for plotting by
providing each geom with a different dataframe.
ggplot() + # we do not want global mapping or data for this plot so none is put in the ggplot call
geom_col(data = irisSummary, # set the dataframe for the columns
aes(x = Species,
y = mean.Petal.Length,
fill = Species),
alpha = 0.5) +
geom_errorbar(data = irisSummary, # set the dataframe for the error bars
aes(x = Species,
ymin = (mean.Petal.Length - standard.deviation), # set the minimum error bar value
ymax = (mean.Petal.Length + standard.deviation)), # set the maximum error bar value
width = 0.2) +
geom_jitter(data = iris, # set the dataframe for the points
aes(x = Species,
y = Petal.Length,
color = Species),
width = 0.2, # make the total spread of the points narrower
shape = 1) # set the shape to open circle

Now we can see both the mean and individual values on our bar
plot.
Another, more streamlined, way of doing this is using
stat_summary, where we remove the need to create a separate
dataframe by using functions within the ggplot package.
ggplot(data = iris,
aes(x = Species,
y = Petal.Length)) +
stat_summary(geom = "col", # identify which geom we want
fun.data = mean_se, # tell stat_summary which function to apply to summarise the data
aes(fill = Species), # set aesthetics as normal
alpha = 0.5) +
stat_summary(geom = "errorbar",
fun.data = mean_se,
color = "black",
width = 0.2) +
geom_jitter(aes(color = Species),
shape = 1,
width = 0.2)

Voilà! We have almost same plot as above but with a step
removed. However, you may have noticed that we used function
mean_se, which calculates the mean and standard error for a
vector of y values at each unique x value
(i.e. the function receives a vector of values for Petal.Length
for each Species) and most of the time we like to use standard
deviation. stat_summary does not offer this function as
part of the package - so what do we do? Create our own.
mean.sd <- function(x){
tibble(y = mean(x), # tell the function that we want a tibble output (similar to dataframe)
ymin = y - sd(x), # calculates the minimum value for error bar
ymax = y + sd(x)) # calculates the maximum value for error bar
}
Now we can create our plot:
ggplot(data = iris,
aes(x = Species,
y = Petal.Length)) +
stat_summary(geom = "col",
fun.data = mean.sd,
aes(fill = Species),
alpha = 0.5) +
stat_summary(geom = "errorbar",
fun.data = mean.sd,
color = "black",
width = 0.2) +
geom_jitter(aes(color = Species),
shape = 1,
width = 0.2)

Facets
Faceting is a technique that allows us to separate data out into
panels based on a variable in the dataframe. This is useful for
visualizing complex data where it may be easier to see patterns when the
data are separated.
There are two methods to create facets in a plot:
facet_wrap() and facet_grid(). If you are only
creating facets based on one variable (e.g. species) you can use
facet_wrap() but if you have a more complex plot where you
want to create facets based on two variables (e.g. species and
time point) you need to use facet_grid().
Let’s pull up another of R’s built-in datasets (mtcars)
that will allow us to see both of these in action. mtcars is built from
data extracted from the 1974 Motor Trend US magazine, and comprises fuel
consumption and 10 aspects of automobile design and performance for 32
automobiles (1973–74 models).
head(mtcars)
Let’s look at mpg (Miles per US Gallon) plotted against hp (Gross
horsepower).
ggplot(data = mtcars,
aes(x = hp,
y = mpg,
color = mpg)) +
geom_point(size = 3)

Now let’s use facet_wrap() to split these data up by vs
(Engine shape, 0 = V, 1 = straight).
ggplot(data = mtcars,
aes(x = hp,
y = mpg,
color = mpg)) +
geom_point(size = 3) +
facet_wrap(~ vs)

Let’s add another variable facet with facet_grid() and
split the data by am (Transmission, 0 = automatic, 1 = manual) as
well.
ggplot(data = mtcars,
aes(x = hp,
y = mpg,
color = mpg)) +
geom_point(size = 3) +
facet_grid(cols = vars(vs), # assign a variable to the column panels
rows = vars(am)) # assign a variable to the row panels

We can see that there are different correlations between hp and mpg
depending on the other qualities of the car. However, this plot is now
difficult to read because both variables are binaries, meaning it’s hard
to tell what’s what. Let’s tidy up these plots and add some labels.
Changing the panel labels without changing the underlying data is
slightly more complex than changing axis titles, so let’s look at how to
do that.
vsLabs <- c("0" = "V-shaped",
"1" = "Straight") # create a vector that matches the binary variables to their values
amLabs <- c("0" = "Automatic",
"1" = "Manual") # do the same for the am variable
ggplot(data = mtcars,
aes(x = hp,
y = mpg,
color = mpg)) +
geom_point(size = 3) +
facet_grid(cols = vars(vs),
rows = vars(am),
labeller = labeller(.cols = vsLabs, # use the labeller function to assign these labels to the rows and columns of the plot
.rows = amLabs)) -> facetPlot
facetPlot

Let’s tidy the rest of this plot up and then save it to file.
facetPlot +
scale_color_gradient(name = "Miles per\nUS Gallon", # \n starts a new line in the legend title
high = "purple",
low = "orange") + # change color of scale
theme_bw() +
xlab("Gross horsepower") + # add x axis title
ylab("Miles per US Gallon") + # add y axis title
theme(strip.background = element_rect(fill = "white")) -> facetPlotTidy # remove grey background from panel titles
facetPlotTidy

Now we can use the same methods as earlier to save our plot to either
a PDF or image file (or both!).
pdf("multi.facet.mtcars.plot.pdf", # file name to write to
height = 4, # plot height in inches
width = 6) # plot width in inches
facetPlotTidy # tell R which plot to write to file
dev.off() # this tells R that you're done creating a file
null device
1
ggsave(plot = facetPlotTidy, # specify plot
"multi.facet.mtcars.plot.tiff", # specify file name
height = 4, # plot height
width = 6, # plot width
units = c("in"), # specify which units to use for height and width
device = "tiff") # specify file type for saving - ggsave will also guess depending on the extension used in file name
Plotting a time course
For many experiments, it’s important to be able to plot a time
course. Let’s load in some example colony count data from an experiment
growing four species of bacteria in both high and low iron conditions,
with time points at 0, 6, and 24 hours.
read.csv("cfu_counts_raw.csv") -> counts # read in counts
Let’s take a quick look at the format of the data we just loaded and
check that the format looks correct for plotting.
head(counts)
Our dataframe has columns as variables and rows as
observations so we’re good to go!
In order to plot a time course as a discrete variable that runs along
the x axis, we need to change the time variable from
numeric to a factor in both the raw counts and group means dataframes.
Factors can help us control the order in which observations are plotted.
By default, ggplot will plot numeric variables in ascending order and
character or factor variables in alphabetical order. So, we’ll also set
the iron level as a factor because I want to plot the low iron condition
before the high iron condition.
counts$time <- factor(counts$time)
counts$iron <- factor(counts$iron,
levels = c("Low iron","High iron"))
Now we can set our custom colors for the plot.
speciesCols <- c("Pseudomonas aeruginosa" = "#43ba8f",
"Staphylococcus aureus" = "#fec44f",
"Streptococcus sanguinis" = "#4292c6",
"Burkholderia orbicola" = "#d57bd4")
Let’s create a line plot of log10 CFU/mL per species over time, with
facets showing the high and low iron. We will plot a ribbon that
represents the standard deviation (sd), thin lines that
represent each replicate (tech.rep), and a thick line
representing the mean CFU/mL for each species (mean.cfu).
We’ll utilize the stat_summary function that we saw
earlier.

Activities
Green 1
Create a scatter plot using the columns Sepal.Length (x) and
Sepal.Width (y) from the iris dataset.
Green 2
Make a plot where all the points are green, and the line is colored
by the species of iris.
Blue 1
Make a plot that includes regression lines for individual species as
well as the overall data.

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdCIKYXV0aG9yOiAiWWFzbWluIEhpbGxpYW0sIFBoRCIKZGF0ZTogIjIwMjUtMDctMDUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSkKYGBgCgpXaGlsc3QgYmFzZSBgUmAgcGxvdHMgYXJlIHF1aWNrIGFuZCB1c2VmdWwgZm9yIGV4YW1pbmluZyBvdXIgZGF0YSwgdGhleSBkb24ndCBhbHdheXMgb2ZmZXIgdGhlIGZsZXhpYmlsaXR5IGFuZCBhdHRyYWN0aXZlIGN1c3RvbWl6YXRpb24gb3B0aW9ucyB0aGF0IHdlJ2QgbGlrZSBmb3IgYSBwcmVzZW50YXRpb24gb3IgbWFudXNjcmlwdC4gVGhpcyBpcyB3aGVyZSBgZ2dwbG90MmAgY29tZXMgaW4uCgpUaGlzIHNlc3Npb24gd2lsbCB0ZWFjaCB5b3UgdGhlIGJhc2ljcyBvZiB1c2luZyBgZ2dwbG90MmAgdG8gdmlzdWFsaXplIGRhdGEgaW4gUi4gZ2dwbG90IHdhcyBkZXZlbG9wZWQgaW4gMjAwNSBieSBIYWRsZXkgV2lja2hhbSBhcyBhbiBvcGVuIHNvdXJjZSBkYXRhIHZpc3VhbGl6YXRpb24gcGFja2FnZSBmb3IgUi4gV2l0aCBgZ2dwbG90MmAsIHlvdSBjYW4gY3JlYXRlIHBsb3RzIHRoYXQgcmFuZ2UgZnJvbSBzaW1wbGUgc2NhdHRlciBkaWFncmFtcyB0byBjb21wbGV4IGN1c3RvbSBwbG90cyB0aGF0IGFyZSAoYWxtb3N0KSBjb21wbGV0ZWx5IGN1c3RvbWl6YWJsZS4KClJlc291cmNlczoKCjxodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vZ3VpZGUtdG8tZGF0YS12aXN1YWxpemF0aW9uLXdpdGgtZ2dwbG90Mi1pbi1hLWhvdXItNjM0YzdlM2JjOWRkPgoKPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS8+CgpbaHR0cHM6Ly93d3cueW91dHViZS5jb20vXEBSaWZmb21vbmFzXShodHRwczovL3d3dy55b3V0dWJlLmNvbS9AUmlmZm9tb25hcyl7LnVyaX0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBJbnN0YWxsIG5lY2Vzc2FyeSBwYWNrYWdlcwoKYGdncGxvdDJgIGlzIGluY2x1ZGVkIGluIHRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlLCBzbyB3ZSBjYW4gc2ltcGx5IGluc3RhbGwgYW5kIGxvYWQgYHRpZHl2ZXJzZWAgZm9yIGV2ZXJ5dGhpbmcgd2UnbGwgY292ZXIgaW4gdGhpcyBzZXNzaW9uLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KcGFja2FnZXNfdG9faW5zdGFsbCA8LSBjKCJ0aWR5dmVyc2UiLCJnZ3RleHQiKQoKZm9yIChwYWNrYWdlIGluIHBhY2thZ2VzX3RvX2luc3RhbGwpIHsKICBpZiAoIShwYWNrYWdlICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgcHJpbnQocGFzdGUoIkluc3RhbGxlZCBwYWNrYWdlOiIsIHBhY2thZ2UpKQogIH0gZWxzZSB7CiAgICBwcmludChwYXN0ZShwYWNrYWdlLCAiaXMgYWxyZWFkeSBpbnN0YWxsZWQiKSkKICB9Cn0KCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGV4dCkKYGBgCgojIyMjIFVuZGVyc3RhbmRpbmcgZ2dwbG90IHN5bnRheAoKVGhlcmUgYXJlIHRocmVlIGZ1bmRhbWVudGFsIGVsZW1lbnRzIHRoYXQgZ28gaW50byBjb25zdHJ1Y3RpbmcgYSBwbG90IHdpdGggYGdncGxvdDJgOgoKKipEYXRhKiogLSBkYXRhZnJhbWUgdG8gYmUgcGxvdHRlZCBgZGF0YSA9IGRhdGFmcmFtZWAKCioqQWVzdGhldGljcyoqIC0gbWFwcyB2YXJpYWJsZXMgdG8gZWxlbWVudHMgb2YgdGhlIHBsb3QgKGkuZS4geCBheGlzLCB5IGF4aXMsIGNvbG9yIHNjaGVtZSwgZXRjLikgYG1hcHBpbmcgPSBhZXMoKWAKCioqR2VvbWV0cnkvTGF5ZXJzKiogLSB2aXN1YWwgZWxlbWVudHMgdXNlZCBmb3IgdGhlIGRhdGEgYCsgZ2VvbV9mdW5jdGlvbigpYAoKVGhlIHR5cGljYWwgaW5wdXQgY29kZSBmb3IgZ2dwbG90IHdpbGwgdXN1YWxseSBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGRmLCAjIGlucHV0IGRhdGEKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHZhcjEsICMgaW5wdXQgbWFwcGluZyBhZXN0aGV0aWNzCiAgICAgICAgICAgICAgICAgICAgIHkgPSB2YXIyLAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHZhcjMpKSArCiAgZ2VvbV9wb2ludCgpICMgYWRkIHBsb3R0aW5nIGxheWVyCmBgYAoKIyMjIyBTZXR0aW5nIHVwIGRhdGEgZm9yIHN1Y2Nlc3MKCk9uZSBvZiB0aGUgbW9zdCBpbXBvcnRhbnQgcGFydHMgb2YgZ2V0dGluZyByZWFkeSB0byBwbG90IGRhdGEgaW4gYFJgIGlzIGVuc3VyaW5nIHRoYXQgeW91ciBkYXRhIGFyZSAidGlkeSIuIFdoZW4gcGFzc2luZyBpbnN0cnVjdGlvbnMgdG8gYGdncGxvdDJgLCB0aGUgcHJvZ3JhbSBpbnRlcnByZXRzIGRhdGFmcmFtZXMgaW4gYSBmaXhlZCB3YXk6CgoqKmNvbHVtbnMqKiBhcmUgdmFyaWFibGVzCgoqKnJvd3MqKiBhcmUgb2JzZXJ2YXRpb25zCgpMZXQncyBleGFtaW5lIGEgZGF0YWZyYW1lIHRvIGJldHRlciB1bmRlcnN0YW5kIGhvdyBgZ2dwbG90MmAgaW50ZXJwcmV0cyBkYXRhLiBIZXJlLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBJcmlzIGRhdGFzZXQuIFRoZSBJcmlzIGRhdGFzZXQgaXMgYnVpbHQtaW4gdG8gYFJgIGFuZCB3YXMgaW50cm9kdWNlZCBieSBCcml0aXNoIHN0YXRpc3RpY2lhbiBhbmQgYmlvbG9naXN0IFJvbmFsZCBBLiBGaXNoZXIgaW4gMTkzNi4gRmlzaGVyIGNvbGxlY3RlZCB0aGUgZGF0YSB0byBzdHVkeSB0aGUgdmFyaWF0aW9uIGluIGlyaXMgZmxvd2VycyBvZiB0aHJlZSBkaWZmZXJlbnQgc3BlY2llczogSXJpcyBzZXRvc2EsIElyaXMgdmVyc2ljb2xvciwgYW5kIElyaXMgdmlyZ2luaWNhLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0KaGVhZChpcmlzKSAjIHZpZXcgdGhlIGZpcnN0IHNpeCByb3dzIG9mIHRoZSBkYXRhZnJhbWUKYGBgCgpMb29raW5nIGF0IHRoZSBmaXJzdCByb3dzIG9mIHRoaXMgZGF0YWZyYW1lIHdlIGNhbiBzZWUgdGhhdCBlYWNoICoqdmFyaWFibGUqKiBpcyBjb250YWluZWQgaW4gYSBjb2x1bW4gYW5kIGVhY2ggcm93IGlzIGFuICoqb2JzZXJ2YXRpb24qKi4gVGhpcyBtZWFucyB0aGF0IGlmIHlvdSBoYXZlIHJlcGxpY2F0ZSBtZWFzdXJlbWVudHMgKGFzIGluIHRoaXMgZGF0YXNldCwgdGhlcmUgYXJlIG11bHRpcGxlIG1lYXN1cmVtZW50cyBvZiBlYWNoIHZhcmlhYmxlIHBlciBzcGVjaWVzKSB5b3Ugd2lsbCBuZWVkIHRvIGhhdmUgYSByb3cgKnBlciByZXBsaWNhdGUqIHJhdGhlciB0aGFuIHN0b3JpbmcgdGhlIHJlcGxpY2F0ZSBkYXRhIGluIGNvbHVtbnMuCgpPdGhlciBpbXBvcnRhbnQgbm90ZXMgYWJvdXQgdGhpcyBkYXRhc2V0OgoKLSAgIGl0IGNvbnNpc3RzIG9mIGZvdXIgbnVtZXJpYyB2YXJpYWJsZXMgKFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgsIFBldGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgpIGFuZCBvbmUgY2F0ZWdvcmljYWwgdmFyaWFibGUgKFNwZWNpZXMpLiBUaGlzIHN0cnVjdHVyZWQgZm9ybWF0IG1ha2VzIGl0IGVhc3kgdG8gbWFwIHZhcmlhYmxlcyB0byBhZXN0aGV0aWNzIGluIGBnZ3Bsb3QyYC4KCi0gICB0aGUgSXJpcyBkYXRhc2V0IGhhcyBhIGJhbGFuY2VkIGNsYXNzIGRpc3RyaWJ1dGlvbi4gRWFjaCBvZiB0aGUgdGhyZWUgc3BlY2llcyAoc2V0b3NhLCB2ZXJzaWNvbG9yLCB2aXJnaW5pY2EpIGhhcyBhbiBlcXVhbCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLiBUaGlzIGJhbGFuY2UgYWxsb3dzIGZvciBmYWlyIHZpc3VhbCBjb21wYXJpc29ucyBhbmQgYXZvaWRzIHBvdGVudGlhbCBiaWFzZXMgdGhhdCBjYW4gYXJpc2UgZnJvbSBpbWJhbGFuY2VkIGRhdGFzZXRzLgoKLSAgIGNvbHVtbiBuYW1lcyBhbmQgZmVhdHVyZXMgY29udGFpbiBubyBzcGFjZXMgb3IgIi0iLiBgUmAgZG9lc24ndCB1c3VhbGx5IGxpa2UgdGhlc2UuCgojIyMjIExldCdzIGJ1aWxkIGEgcGxvdCEKCmBnZ3Bsb3QyYCBidWlsZHMgcGxvdHMgaW4gbGF5ZXJzLiBZb3UgY2FuIHN0YXJ0IHdpdGggYSBsYXllciBzaG93aW5nIHJhdyBkYXRhLCB0aGVuIGNvbnRpbnVlIHRvIGFkZCB1cCBhZGRpdGlvbmFsIGVsZW1lbnRzIHRvIHByb2R1Y2UgeW91ciBkZXNpcmVkIGdyYXBoLiBUaGlzIGFwcHJvYWNoIHdpbGwgaGVscCB5b3UgcmVkdWNlIHRoZSBnYXAgYmV0d2VlbiB0aGUgZXhwZWN0ZWQgb3V0Y29tZXMgaW4geW91ciBoZWFkIGFuZCB0aGUgcGxvdHMgaW4gcmVhbGl0eS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsICMgdXNlICdkYXRhJyBhcmd1bWVudCB0byB0ZWxsIGdncGxvdCB3aGljaCBkYXRhZnJhbWUgd2Ugd2FudCB0byBwbG90IGZyb20KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwgIyBtYXBwaW5nIGRldGVybWluZXMgd2hpY2ggdmFyaWFibGVzIGFyZSBhc3NpZ25lZCB0byBwbG90IGVsZW1lbnRzCiAgICAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCkpIC0+IGJhc2ljUGxvdAoKYmFzaWNQbG90CmBgYAoKV2UgaGF2ZSB0b2xkIGBnZ3Bsb3QyYCB3aGljaCB2YXJpYWJsZXMgd2Ugd2FudCB0byBwbG90IG9uIHRoZSB4IGFuZCB5IGF4ZXMgYnV0IHdlIGhhdmUgbm90IHRvbGQgYGdncGxvdDJgIHdoaWNoIGdlb21ldHJpYyBlbGVtZW50cyAoaS5lLiBnZW9tcykgdG8gdXNlIHRvIGNvbnN0cnVjdCB0aGUgcGxvdCwgc28gYWxsIHdlIGhhdmUgYXJlIHRoZSBheGVzIGFuZCBhIGJsYW5rIHBsb3QuCgojIyMjIFdoYXQgYXJlIGdlb21zPwoKR2VvbXMgYXJlIHRoZSBnZW9tZXRyaWMgb2JqZWN0cyAoZS5nLiBsaW5lcywgYmFycywgZXRjLikgdGhhdCBkZXRlcm1pbmUgaG93IG9ic2VydmF0aW9ucyBhcmUgcmVuZGVyZWQuIExheWVyaW5nIGVsZW1lbnRzIGluIGEgcGxvdCB1c3VhbGx5IHN0YXJ0cyB3aXRoIGFkZGluZyBnZW9tcy4gTGV0J3MgYWRkIGBnZW9tX3BvaW50KClgIHRvIG91ciBiYXNpYyBwbG90IHRvIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdDoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFBldGFsLkxlbmd0aCwgIyBpdCBpcyB2ZXJ5IGNvbW1vbiB0byBzZWUgJ21hcHBpbmcgPScgb21pdHRlZCBmcm9tIHRoZSBjb2RlIC0gZ2dwbG90IHdpbGwgYWNjZXB0IGVpdGhlcgogICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCkpICsgIyB1c2UgYSArIHRvIGFkZCBlbGVtZW50cyB0byB5b3VyIHBsb3QKICBnZW9tX3BvaW50KCkgLT4gYmFzaWNTY2F0dGVyCgpiYXNpY1NjYXR0ZXIKYGBgCgpBbm90aGVyIHdheSB5b3UgY2FuIGFkZCBsYXllcnMgdG8gYSBwbG90IGlzIGJ5IHNpbXBseSBhZGRpbmcgdGhlbSB0byB0aGUgZW5kIG9mIHRoZSBvYmplY3QgdGhhdCB3ZSBhc3NpZ25lZCBvdXIgZmlyc3QgcGxvdCB0by4gSXQgaXMgdmVyeSBjb21tb24gdG8gc2VlIHRoaXMgaW4gb25saW5lIGd1aWRlcyBhbmQgZm9ydW1zIChzdWNoIGFzIFN0YWNrIE92ZXJmbG93KSB3aGVyZSB5b3UgbWlnaHQgbG9vayBmb3IgaGVscCB3aXRoIGBSYCBjb2Rpbmc6CgpgYGB7cn0KYmFzaWNQbG90ICsKICBnZW9tX3BvaW50KCkKYGBgCgpBbHRob3VnaCB0aGlzIGdlbmVyYXRlcyB0aGUgc2FtZSBvdXRwdXQsIEkgd291bGQgZ2VuZXJhbGx5IGF2b2lkIG1ha2luZyB5b3VyIHBsb3RzIHRoaXMgd2F5IC0gaWYgeW91IGVuZCB1cCB3aXRoIHNvbWV0aGluZyB0aGF0IGlzbid0IHF1aXRlIHdvcmtpbmcgYXMgZXhwZWN0ZWQgSSBmaW5kIGl0IGNhbiBiZSBlYXNpZXIgdG8gZml4IGlmIGFsbCB5b3VyIGNvZGUgaXMgbGFpZCBvdXQgaW4gZnJvbnQgb2YgeW91LCByYXRoZXIgdGhhbiBoYXZpbmcgdG8gcmV2aXNpdCBlYWNoIGluZGl2aWR1YWwgc3RlcCBpbiB0aGUgcHJvY2VzcyBvZiBtYWtpbmcgeW91ciBwbG90LgoKTm93IHdlJ2xsIHN0YXJ0IHRvIGFkZCBzb21lIG1vcmUgZWxlbWVudHMgdG8gb3VyIG1hcHBpbmcgYWVzdGhldGljcyB0byBiZXR0ZXIgaWxsdXN0cmF0ZSBvdXIgZGF0YS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKKipJTVBPUlRBTlQgTk9URSoqCgpUaGUgaW5pdGlhbCBtYXBwaW5nIHRoYXQgeW91IHNwZWNpZnkgaW4gdGhlIGBnZ3Bsb3QyYCBjb21tYW5kIChpLmUuIGF4ZXMsIGNvbG9yLCBzaXplLCBldGMuKSBhcmUgYnkgZGVmYXVsdCB1c2VkIGdsb2JhbGx5IGZvciB0aGUgcGxvdCBhbmQgYXJlIGNhcnJpZWQgb3ZlciB0byBhbnkgZ2VvbXMgeW91IGFkZCBpbiB0aGUgZm9sbG93aW5nIGNvZGUuIEVhY2ggZ2VvbSAqY2FuKiBoYXZlIGl0J3Mgb3duIHNlcGFyYXRlIG1hcHBpbmcgYWVzdGhldGljcywgd2hpY2ggY2FuIGFsbG93IHlvdSB0byBjcmVhdGUgbW9yZSBjb21wbGV4IHBsb3RzLiBJZiB5b3UgZXZlciBydW4gaW50byBpc3N1ZXMgd2hlcmUgYSBnZW9tIGlzIG5vdCBiZWhhdmluZyBhcyB5b3Ugd291bGQgZXhwZWN0LCB0YWtlIGEgbG9vayBiYWNrIHRocm91Z2ggeW91ciBjb2RlIGFuZCBjaGVjayB3aGVyZSB5b3VyIGFlc3RoZXRpY3Mgd2VyZSBhc3NpZ25lZCwgYW5kIGhvdyB0aGV5IGFwcGx5IHRvIHRoZSBnZW9tcyB5b3UgYXJlIHRyeWluZyB0byBsYXllci4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKTGV0J3MgY29sb3Igb3VyIHBvaW50cyBieSBTcGVjaWVzOgoKYGBge3J9CmdncGxvdChkYXRhPWlyaXMsIAogICAgICAgbWFwcGluZyA9IGFlcyh4PVBldGFsLkxlbmd0aCwgCiAgICAgICAgICAgICAgICAgICAgIHk9UGV0YWwuV2lkdGgsIAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMpKSArICMgdGVsbCBnZ3Bsb3QgdGhhdCBjb2xvciBpcyBkZXRlcm1pbmVkIGJ5IFNwZWNpZXMgdmFyaWFibGUKICBnZW9tX3BvaW50KCkgLT4gY29sb3JTY2F0dGVyCiAgICAgICAgICAKY29sb3JTY2F0dGVyCmBgYAoKTm93IHdlIGNhbiBhZGQgYSBzaW1wbGUgcmVncmVzc2lvbiB1c2luZyBgZ2VvbV9zbW9vdGgoKWAgYW5kIHdlIGNhbiBkZW1vbnN0cmF0ZSBob3cgY2hhbmdpbmcgZ2xvYmFsIHZzLiBzcGVjaWZpYyBhZXN0aGV0aWNzIGFmZmVjdCBnZW9tcy4gU29tZSBnZW9tcyBoYXZlIHNwZWNpYWxpemVkIGFyZ3VtZW50cyB0aGF0IGFsbG93IHRoZW0gdG8gZnVuY3Rpb24uIEluIHRoaXMgY2FzZSwgYGdlb21fc21vb3RoKClgIGFsbG93cyB1cyB0byB0ZWxsIGl0IHdoaWNoIG1ldGhvZCB0byB1c2UgdG8gZ2VuZXJhdGUgdGhlIGN1cnZlIHRoYXQgaXQgd2lsbCBwbG90LiBXZSB3aWxsIG9wdCBmb3IgYGxtYCB3aGljaCBpcyBhIGxpbmVhciBtb2RlbC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1pcmlzLCAKICAgICAgIG1hcHBpbmcgPSBhZXMoeD1QZXRhbC5MZW5ndGgsIAogICAgICAgICAgICAgICAgICAgICB5PVBldGFsLldpZHRoLCAKICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTcGVjaWVzKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSAtPiBjb2xvclNjYXR0ZXJsbQogICAgICAgICAgCmNvbG9yU2NhdHRlcmxtCmBgYAoKVGhpcyBjcmVhdGVzIHRocmVlIHNlcGFyYXRlIGN1cnZlcyB0aGF0IG1hcCB0byB0aGUgcG9pbnRzIGZyb20gZWFjaCBTcGVjaWVzIGJ5IGNvbG9yLCBhcyB0aGlzIGlzIHdoYXQgaXMgc3BlY2lmaWVkIGluIHRoZSBnbG9iYWwgYWVzdGhldGljcy4gTGV0J3MgY2hhbmdlIHRoYXQgYW5kIHBsb3QgYSBjdXJ2ZSB0aGF0IHNwYW5zIGFsbCBwb2ludHM6CgpgYGB7cn0KZ2dwbG90KGRhdGE9aXJpcywgCiAgICAgICBtYXBwaW5nID0gYWVzKHg9UGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICAgICAgICAgICAgeT1QZXRhbC5XaWR0aCkpICsgIyByZW1vdmUgY29sb3IgZnJvbSBnbG9iYWwgYWVzdGhldGljcwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3BlY2llcykpICsgIyBzZXQgZ2VvbV9wb2ludCBhZXN0aGV0aWNzIC0gdGhpcyB3aWxsIG9ubHkgY29sb3IgcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pIC0+IGNvbG9yU2NhdHRlcmxtMgoKY29sb3JTY2F0dGVybG0yCmBgYAoKTm93IHdlIGNhbiBzZWUgdGhhdCB0aGUgcG9pbnRzIGFyZSBzdGlsbCBjb2xvcmVkIGJ5IFNwZWNpZXMsIGJ1dCB0aGUgcmVncmVzc2lvbiBpcyBub3QuCgpMZXQncyBwbGF5IHdpdGggc29tZSBtb3JlIGFlc3RoZXRpY3M6CgpgYGB7cn0KZ2dwbG90KGRhdGE9aXJpcywgCiAgICAgICBtYXBwaW5nID0gYWVzKHg9UGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICAgICAgICAgICAgeT1QZXRhbC5XaWR0aCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAiYmx1ZSIpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSAtPiBiYXNpY1Bsb3RCbHVlCgpiYXNpY1Bsb3RCbHVlCmBgYAoKTm90aWNlIGhvdyBldmVuIHRob3VnaCB3ZSBoYXZlIGNoYW5nZWQgdGhlIGFlc3RoZXRpYyBvZiB0aGUgcG9pbnRzIHRvIGJlICJibHVlIiwgaXQgaGFzIG5vdCBtYWRlIHRoZW0gYmx1ZS4gSWYgd2Ugd2FudCB0byBtYWtlIGFsbCB0aGUgcG9pbnRzIG9uZSBjb2xvciAob3IgYSBkaWZmZXJlbnQgc2hhcGUsIG9yIGEgZGlmZmVyZW50IHNpemUpIHRoZXNlIGFyZSAqbm90KiBzZXQgYnkgYWVzdGhldGljcywgYXMgdGhleSBhcmUgbm90IGRlcGVuZGVudCBvbiBhIHZhcmlhYmxlLgoKTGV0J3MgbWFrZSBvdXIgcG9pbnRzIGJsdWUgYW5kIGNoYW5nZSB0aGVpciBzaGFwZToKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCAjIG5vdGUgdGhhdCB0aGVzZSBvcHRpb25zIGFyZSBub3QgcGFyc2VkIHRocm91Z2ggdGhlIGFlcygpIGFyZ3VtZW50CiAgICAgICAgICAgICBzaXplID0gNSwKICAgICAgICAgICAgIHNoYXBlID0gMSkgLT4gb3BlbkNpcmNsZUJsdWUKCm9wZW5DaXJjbGVCbHVlCgojIHNoYXBlcyBhcmUgZGVmaW5lZCBieSBhIG51bWVyaWNhbCB2YWx1ZQojIGF2YWlsYWJsZSBzaGFwZXMgY2FuIGJlIHZpZXdlZCBhdCBodHRwczovL3d3dy5kYXRhbm92aWEuY29tL2VuL2Jsb2cvZ2dwbG90LXBvaW50LXNoYXBlcy1iZXN0LXRpcHMvIG9yIGJ5IHVzaW5nIGdncHVicjo6c2hvd19wb2ludF9zaGFwZXMoKQpgYGAKClRoZXJlIGFyZSBvdGhlciB3YXlzIHdlIGNhbiBjb250cm9sIGhvdyBlYWNoIGxheWVyIGlzIHJlbmRlcmVkLiBMZXQncyBzdGFydCB3aXRoIGNvbnRyb2xsaW5nICJzY2FsZXMiLiBTY2FsZXMgYWxsb3cgdXMgdG8gZWRpdCBzcGVjaWZpYyBlbGVtZW50cyBvZiB0aGUgYWVzdGhldGljcyBhbmQgYXJlIG5hbWVkIGluIGEgdW5pZm9ybSBtYW5uZXIgdGhhbiBkZXNjcmliZXMgaG93IHRoZXkgYWN0IGFuZCB3aGF0IHRoZXkgYWZmZWN0LiBUaGUgbmFtZXMgYXJlIG1hZGUgdXAgb2YgdGhyZWUgcGllY2VzIHNlcGFyYXRlZCBieSAiXF8iOgoKLSAgIGBzY2FsZWAKLSAgIHRoZSBuYW1lIG9mIHRoZSBhZXN0aGV0aWMgKGUuZy4gYGNvbG9yYCwgYHNoYXBlYCwgYHNpemVgLCBldGMuKQotICAgdGhlIG5hbWUgb2YgdGhlIHNjYWxlIChlLmcuIGBtYW51YWxgLCBgY29udGludW91c2AsIGBkaXNjcmV0ZWAsIGV0Yy4pCgpXaXRoaW4gdGhlIHNjYWxlcyB0aGVyZSBhcmUgc2V2ZXJhbCBvcHRpb25zIHdlIGNhbiBlZGl0OgoKLSAgIGBuYW1lID1gIHRoaXMgY29udHJvbHMgd2hhdCB0aGUgdmFyaWFibGUgaXMgY2FsbGVkIHdpdGhpbiB0aGUgcGxvdCBvciBsZWdlbmQKLSAgIGB2YWx1ZXMgPWAgdGhpcyBhbGxvd3MgdXMgdG8gbWFudWFsbHkgaW5wdXQgdGhlIHZhcmlhYmxlcyAoaS5lLiBjb2xvcnMgb3Igc2hhcGVzKSB1c2VkIGluIHRoZSBwbG90Ci0gICBgbGFiZWxzID1gIHRoaXMgY29udHJvbHMgdGhlIGRhdGEgbGFiZWxzIChpLmUuIHNwZWNpZXMgbmFtZXMpIGluIHRoZSBwbG90IG9yIGxlZ2VuZAoKTGV0J3MgdXNlIGBzY2FsZV9jb2xvcl9tYW51YWxgIHRvIG1hbnVhbGx5IHNlbGVjdCBzb21lIGNvbG9ycyBmb3Igb3VyIHBsb3QuCgooVGhlcmUgYXJlIHNldCBuYW1lZCBjb2xvcnMgdGhhdCBjYW4gYmUgdXNlZCBpbiBgUmAgPGh0dHBzOi8vc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmPiBidXQgeW91IGNhbiBhbHNvIHVzZSBoZXggY29kZXMuIE1ha2Ugc3VyZSB0byB1c2UgY29sb3IgYmxpbmQgZnJpZW5kbHkgY29sb3Igc2NoZW1lcyBmb3IgZmlndXJlcyB5b3UgcGxhbiB0byBwcmVzZW50IG9yIHB1Ymxpc2ghKQoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLldpZHRoLAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIklyaXMgc3BlY2llcyIsCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoInNldG9zYSIgPSAicGluayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZlcnNpY29sb3IiID0gInBsdW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2aXJnaW5pY2EiPSJzZWFncmVlbjMiKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiSXJpcyBzZXRvc2EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJcmlzIHZlcnNpY29sb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJJcmlzIHZpcmdpbmljYSIpKSAtPiBtdWx0aUNvbG9yCgptdWx0aUNvbG9yCmBgYAoKTm93IHdlIGhhdmUgc29tZSBkaWZmZXJlbnQgY29sb3JzIGFuZCBkYXRhIGxhYmVscyBpbiBvdXIgcGxvdCBvZiB0aGUgSXJpcyBkYXRhLiBUaGUgYG5hbWVgIGFuZCBgbGFiZWxzYCBvcHRpb25zIGluIHRoZSBgc2NhbGVgIGFyZSB1c2VmdWwgZm9yIGNoYW5naW5nIGhvdyB0aGUgZGF0YSBhcmUgbGFiZWxlZCBpbiB5b3VyIHBsb3QgKndpdGhvdXQqIG5lZWRpbmcgdG8gbWFuaXB1bGF0ZSB0aGUgcmF3IGRhdGEuCgpXZSBjYW4gYWxzbyB1c2UgY29udGludW91cyBjb2xvciBzY2FsZXMgdG8gdmlzdWFsbHkgcmVwcmVzZW50IGNoYW5nZXMgaW4gdmFsdWVzOgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gUGV0YWwuTGVuZ3RoKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSAtPiBibHVlQ29udAoKYmx1ZUNvbnQKYGBgCgpZb3UgY2FuIGFsc28gdXNlIG90aGVyIHZhcmlhYmxlcyB3aXRoaW4gdGhlIGRhdGFmcmFtZSB0byBjb250cm9sIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU2VwYWwuTGVuZ3RoLCAjIGNvbG9yIGlzIGRlcGVuZGVudCBvbiBzZXBhbCBsZW5ndGgKICAgICAgICAgICAgICAgICBzaXplID0gU2VwYWwuV2lkdGgpKSArICMgcG9pbnQgc2l6ZSBpcyBkZXBlbmRlbnQgb24gc2VwYWwgd2lkdGgKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwKICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgc2UgPSBGQUxTRSkgKyAjIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGN1cnZlCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoaGlnaCA9ICJwdXJwbGUiLAogICAgICAgICAgICAgICAgICAgICAgIGxvdyA9ICJvcmFuZ2UiKSAjIG1hbnVhbGx5IHNldCB0aGUgY29sb3JzIG9mIHRoZSBncmFkaWVudCBzY2FsZQpgYGAKCk9idmlvdXNseSB0aGVyZSBpcyBhIGxpdHRsZSB0b28gbXVjaCBkYXRhIG5vdyBjb250YWluZWQgaW4gdGhpcyBwbG90IGZvciBpdCB0byBiZSBwYXJ0aWN1bGFybHkgdXNlZnVsLCBidXQgaXQgaXMgYSBnb29kIGV4YW1wbGUgb2YgaG93IG11Y2ggZGF0YSB5b3UgY2FuIGRpc3BsYXkgYW5kIHRoZSBkaWZmZXJlbnQgd2F5cyB5b3UgY2FuIHByZXNlbnQgaXQgdXNpbmcgYFJgLgoKTGV0J3MgdGlkeSB1cCBvdXIgcGxvdCBhbmQgbWFrZSBzb21ldGhpbmcgdGhhdCBsb29rcyBhIGxpdHRsZSBtb3JlICJwdWJsaWNhdGlvbi1yZWFkeSI6CgpGaXJzdCwgbGV0J3MgYXNzaWduIG91ciBjaG9zZW4gY29sb3Igc2NhbGUgdG8gYSB2ZWN0b3Igb2JqZWN0IHNvIHdlIGNhbiBjYWxsIHRoZSBzYW1lIGNvbG9ycyBmb3IgYW55IGZ1dHVyZSBwbG90cyB3aXRob3V0IG5lZWRpbmcgdG8gd3JpdGUgb3V0IHRoZSBjb2RlIGV2ZXJ5IHRpbWUuIFRoaXMgdGltZSBJJ20gZ29pbmcgdG8gdXNlIGEgY29sb3IgYmxpbmQgZnJpZW5kbHkgcGFsZXR0ZSBnZW5lcmF0ZWQgdXNpbmcgdGhpcyB0b29sOiA8aHR0cHM6Ly9kYXZpZG1hdGhsb2dpYy5jb20vY29sb3JibGluZD4uCgpgYGB7cn0KcGxvdENvbG9ycyA8LSBjKCJzZXRvc2EiID0gIiM2NDhGRkYiLAogICAgICAgICAgICAgICAgInZlcnNpY29sb3IiID0gIiNEQzI2N0YiLAogICAgICAgICAgICAgICAgInZpcmdpbmljYSI9IiNGRkIwMDAiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sCiAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfYncoKSArICMgdGhpcyBpcyBhIGJ1aWx0LWluIHRoZW1lIHRoYXQgcmVtb3ZlcyB0aGUgZ3JheSBwbG90IGJhY2tncm91bmQKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGxvdENvbG9ycykgKyAjIGRpcmVjdCBnZ3Bsb3QgdG8gb3VyIGNvbG9yIHZlY3RvcgogIHlsYWIoIlBldGFsIHdpZHRoIChtbSkiKSArICMgY2hhbmdlIHkgYXhpcyBsYWJlbCAtIGNhbiBhbHNvIGJlIGRvbmUgd2l0aCBzY2FsZXMKICBsYWJzKHggPSAiUGV0YWwgbGVuZ3RoIChtbSkiLAogIGNvbG9yID0gIklyaXMgc3BlY2llcyIpICsgIyBjaGFuZ2UgbGVnZW5kIHRpdGxlIC0gY2FuIGFsc28gYmUgZG9uZSB3aXRoIHNjYWxlcyBhcyBwcmV2aW91c2x5CiAgZ2d0aXRsZSgiUGV0YWwgd2lkdGggYnkgcGV0YWwgbGVuZ3RoIHBlciBzcGVjaWVzIikgKyAjIGFkZCBwbG90IHRpdGxlIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSAtPiBtdWx0aUNvbG9yVGlkeSAjIGNlbnRlciBwbG90IHRpdGxlCgptdWx0aUNvbG9yVGlkeQpgYGAKClBlcmZlY3QhIE5vdyB3ZSBjYW4gd3JpdGUgb3VyIHBsb3QgdG8gYSBwZGY6CgpgYGB7cn0KcGRmKCJtdWx0aS5jb2xvci5pcmlzLnBsb3QubG0ucGRmIiwgIyBmaWxlIG5hbWUgdG8gd3JpdGUgdG8KICAgIGhlaWdodCA9IDQsICMgcGxvdCBoZWlnaHQgaW4gaW5jaGVzCiAgICB3aWR0aCA9IDYpICMgcGxvdCB3aWR0aCBpbiBpbmNoZXMKCm11bHRpQ29sb3JUaWR5ICMgdGVsbCBSIHdoaWNoIHBsb3QgdG8gd3JpdGUgdG8gZmlsZQoKZGV2Lm9mZigpICMgdGhpcyB0ZWxscyBSIHRoYXQgeW91J3JlIGRvbmUgY3JlYXRpbmcgYSBmaWxlCmBgYAoKT3Igd2UgY2FuIHVzZSBgZ2dzYXZlKClgLCB3aGljaCBpcyBhIGZ1bmN0aW9uIG9mIGBnZ3Bsb3QyYCB0byBzYXZlIGFzIGFueSBvdGhlciBncmFwaGljcyBmaWxlIHR5cGU6CgpgYGB7cn0KZ2dzYXZlKHBsb3QgPSBtdWx0aUNvbG9yVGlkeSwgIyBzcGVjaWZ5IHBsb3QKICAgICAgICJtdWx0aS5jb2xvci5pcmlzLnBsb3QubG0udGlmZiIsICMgc3BlY2lmeSBmaWxlIG5hbWUKICAgICAgIGhlaWdodCA9IDQsICMgcGxvdCBoZWlnaHQKICAgICAgIHdpZHRoID0gNiwgIyBwbG90IHdpZHRoCiAgICAgICB1bml0cyA9IGMoImluIiksICMgc3BlY2lmeSB3aGljaCB1bml0cyB0byB1c2UgZm9yIGhlaWdodCBhbmQgd2lkdGgKICAgICAgIGRldmljZSA9ICJ0aWZmIikgIyBzcGVjaWZ5IGZpbGUgdHlwZSBmb3Igc2F2aW5nIC0gZ2dzYXZlIHdpbGwgYWxzbyBndWVzcyBkZXBlbmRpbmcgb24gdGhlIGV4dGVuc2lvbiB1c2VkIGluIGZpbGUgbmFtZSAKYGBgCgojIyMjIFBsb3R0aW5nIGdyb3VwIG1lYW5zIGFuZCBlcnJvciBiYXJzCgpBbm90aGVyIHdheSB0aGF0IHdlIG1heSB3YW50IHRvIHBsb3Qgb3VyIGRhdGEgaXMgYnkgcGxvdHRpbmcgYm90aCBncm91cCBtZWFucyAqYW5kKiBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzLiBUaGlzIGNhbiBoZWxwIHBlb3BsZSBiZXR0ZXIgdmlzdWFsaXplIHRoZSBzcHJlYWQgb2Ygb3VyIGRhdGEuIFRoaXMgaXMgZWFzeSBlbm91Z2ggd2l0aCBnZW9tcyBsaWtlIGBnZW9tX2JveHBsb3QoKWAgYW5kIGBnZW9tX3Zpb2xpbigpYCB0aGF0IGhhdmUgZ3JvdXAgbWV0cmljcyBidWlsdCBpbnRvIHRoZWlyIGZ1bmN0aW9uYWxpdHkuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgYWVzKHggPSBTcGVjaWVzLAogICAgICAgICAgIHkgPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgZmlsbCA9IFNwZWNpZXMpKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVycyA9IEZBTFNFKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKWW91IGNhbiBzZWUgdGhhdCBgZ2VvbV9ib3hwbG90YCBoYXMgYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgYSBib3ggdGhhdCBkaXNwbGF5cyB0aGUgbWVkaWFuICh0aGljayBsaW5lKSBhbmQgYm94IHRoYXQgc3BhbnMgdGhlIDI1dGggLSA3NXRoIHBlcmNlbnRpbGVzLCB3aXRoIHdoaXNrZXJzIHRoYXQgZXh0ZW5kIHRvIHRoZSBmdXJ0aGVzdCB2YWx1ZSBubyBtb3JlIHRoYW4gMS41IFggdGhlIElRUiBmcm9tIHRoZSBib3guIFZhbHVlcyBiZXlvbmQgdGhlIHdoaXNrZXJzIHdvdWxkIGJlIGNvdW50ZWQgYXMgb3V0bGllcnMgYW5kIHBsb3R0ZWQgc2VwYXJhdGVseS4gVGhpcyBpcyBncmVhdCBmb3IgdGFraW5nIGEgcXVpY2sgc3VtbWFyeSBsb29rIGF0IHlvdXIgZGF0YS4KCkJ1dCB3aGF0IGlmIHdlIHdhbnQgdG8gdXNlIHNvbWV0aGluZyBsaWtlIGBnZW9tX2JhcigpYCB0aGF0IGRvZXMgbm90IGhhdmUgYnVpbHQgaW4gZ3JvdXAgZnVuY3Rpb25hbGl0eT8KClRoZXJlIGFyZSBhIGNvdXBsZSBvZiB3YXlzIHdlIGNvdWxkIHNvbHZlIHRoaXMgaXNzdWUgdXNpbmcgZnVuY3Rpb25zIGluIHRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlLiBGaXJzdCwgd2UgY291bGQgY3JlYXRlIGEgc3VtbWFyeSB0YWJsZSB0aGF0IGNvbnRhaW5zIGdyb3VwZWQgaW5mb3JtYXRpb24gZm9yIG91ciBkYXRhIHVzaW5nIGBzdW1tYXJpc2VgLgoKYGBge3J9CmlyaXMgJT4lCiAgZ3JvdXBfYnkoU3BlY2llcykgJT4lICMgZ3JvdXBfYnkgdGVsbHMgUiB3aGljaCB2YXJpYWJsZSB0byB1c2UgdG8gZ3JvdXAgb2JzZXJ2YXRpb25zCiAgc3VtbWFyaXNlKG1lYW4uUGV0YWwuTGVuZ3RoID0gbWVhbihQZXRhbC5MZW5ndGgpLCAjIGFkZCBhIGNvbHVtbiBjb250YWluaW5nIG1lYW4gdmFsdWVzIHBlciBzcGVjaWVzCiAgICAgICAgICAgIHN0YW5kYXJkLmRldmlhdGlvbiA9IHNkKFBldGFsLkxlbmd0aCkpIC0+IGlyaXNTdW1tYXJ5ICMgYWRkIGEgY29sdW1uIGNvbnRhaW5pbmcgc3RhbmRhcmQgZGV2aWF0aW9uCgpoZWFkKGlyaXNTdW1tYXJ5KQpgYGAKCldlIGNhbiB1c2UgdGhlIGBzdW1tYXJpc2VgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIG5ldyBkYXRhZnJhbWUgdGhhdCBjb250YWlucyBhIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgZWFjaCBzcGVjaWVzLiBXZSBjYW4gd3JpdGUgdGhpcyB0byBhIG5ldyBvYmplY3QgYW5kIHRoZW4gdXNlIHRoaXMgZm9yIHBsb3R0aW5nIGJ5IHByb3ZpZGluZyBlYWNoIGBnZW9tYCB3aXRoIGEgZGlmZmVyZW50IGRhdGFmcmFtZS4KCmBgYHtyfQpnZ3Bsb3QoKSArICMgd2UgZG8gbm90IHdhbnQgZ2xvYmFsIG1hcHBpbmcgb3IgZGF0YSBmb3IgdGhpcyBwbG90IHNvIG5vbmUgaXMgcHV0IGluIHRoZSBnZ3Bsb3QgY2FsbAogIGdlb21fY29sKGRhdGEgPSBpcmlzU3VtbWFyeSwgIyBzZXQgdGhlIGRhdGFmcmFtZSBmb3IgdGhlIGNvbHVtbnMKICAgICAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgICAgIHkgPSBtZWFuLlBldGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgZmlsbCA9IFNwZWNpZXMpLAogICAgICAgICAgIGFscGhhID0gMC41KSArCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gaXJpc1N1bW1hcnksICMgc2V0IHRoZSBkYXRhZnJhbWUgZm9yIHRoZSBlcnJvciBiYXJzCiAgICAgICAgICAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgICAgICAgICAgeW1pbiA9IChtZWFuLlBldGFsLkxlbmd0aCAtIHN0YW5kYXJkLmRldmlhdGlvbiksICMgc2V0IHRoZSBtaW5pbXVtIGVycm9yIGJhciB2YWx1ZQogICAgICAgICAgICAgICAgICAgIHltYXggPSAobWVhbi5QZXRhbC5MZW5ndGggKyBzdGFuZGFyZC5kZXZpYXRpb24pKSwgIyBzZXQgdGhlIG1heGltdW0gZXJyb3IgYmFyIHZhbHVlCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMikgKwogIGdlb21faml0dGVyKGRhdGEgPSBpcmlzLCAjIHNldCB0aGUgZGF0YWZyYW1lIGZvciB0aGUgcG9pbnRzCiAgICAgICAgICAgICAgYWVzKHggPSBTcGVjaWVzLAogICAgICAgICAgICAgICAgICB5ID0gUGV0YWwuTGVuZ3RoLAogICAgICAgICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMpLAogICAgICAgICAgICAgIHdpZHRoID0gMC4yLCAjIG1ha2UgdGhlIHRvdGFsIHNwcmVhZCBvZiB0aGUgcG9pbnRzIG5hcnJvd2VyCiAgICAgICAgICAgICAgc2hhcGUgPSAxKSAjIHNldCB0aGUgc2hhcGUgdG8gb3BlbiBjaXJjbGUKYGBgCgpOb3cgd2UgY2FuIHNlZSBib3RoIHRoZSBtZWFuIGFuZCBpbmRpdmlkdWFsIHZhbHVlcyBvbiBvdXIgYmFyIHBsb3QuCgpBbm90aGVyLCBtb3JlIHN0cmVhbWxpbmVkLCB3YXkgb2YgZG9pbmcgdGhpcyBpcyB1c2luZyBgc3RhdF9zdW1tYXJ5YCwgd2hlcmUgd2UgcmVtb3ZlIHRoZSBuZWVkIHRvIGNyZWF0ZSBhIHNlcGFyYXRlIGRhdGFmcmFtZSBieSB1c2luZyBmdW5jdGlvbnMgd2l0aGluIHRoZSBgZ2dwbG90YCBwYWNrYWdlLgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICB5ID0gUGV0YWwuTGVuZ3RoKSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImNvbCIsICMgaWRlbnRpZnkgd2hpY2ggZ2VvbSB3ZSB3YW50CiAgICAgICAgICAgICAgIGZ1bi5kYXRhID0gbWVhbl9zZSwgIyB0ZWxsIHN0YXRfc3VtbWFyeSB3aGljaCBmdW5jdGlvbiB0byBhcHBseSB0byBzdW1tYXJpc2UgdGhlIGRhdGEKICAgICAgICAgICAgICAgYWVzKGZpbGwgPSBTcGVjaWVzKSwgIyBzZXQgYWVzdGhldGljcyBhcyBub3JtYWwKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJlcnJvcmJhciIsCiAgICAgICAgICAgICAgIGZ1bi5kYXRhID0gbWVhbl9zZSwKICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICB3aWR0aCA9IDAuMikgKwogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IFNwZWNpZXMpLAogICAgICAgICAgICAgIHNoYXBlID0gMSwKICAgICAgICAgICAgICB3aWR0aCA9IDAuMikKYGBgCgpWb2lsw6AhIFdlIGhhdmUgKmFsbW9zdCogc2FtZSBwbG90IGFzIGFib3ZlIGJ1dCB3aXRoIGEgc3RlcCByZW1vdmVkLiBIb3dldmVyLCB5b3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IHdlIHVzZWQgZnVuY3Rpb24gYG1lYW5fc2VgLCB3aGljaCBjYWxjdWxhdGVzIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBlcnJvciBmb3IgYSB2ZWN0b3Igb2YgYHlgIHZhbHVlcyBhdCBlYWNoIHVuaXF1ZSBgeGAgdmFsdWUgKCppLmUuKiB0aGUgZnVuY3Rpb24gcmVjZWl2ZXMgYSB2ZWN0b3Igb2YgdmFsdWVzIGZvciBQZXRhbC5MZW5ndGggZm9yIGVhY2ggU3BlY2llcykgYW5kIG1vc3Qgb2YgdGhlIHRpbWUgd2UgbGlrZSB0byB1c2Ugc3RhbmRhcmQgZGV2aWF0aW9uLiBgc3RhdF9zdW1tYXJ5YCBkb2VzIG5vdCBvZmZlciB0aGlzIGZ1bmN0aW9uIGFzIHBhcnQgb2YgdGhlIHBhY2thZ2UgLSBzbyB3aGF0IGRvIHdlIGRvPyBDcmVhdGUgb3VyIG93bi4KCmBgYHtyfQptZWFuLnNkIDwtIGZ1bmN0aW9uKHgpewogIHRpYmJsZSh5ID0gbWVhbih4KSwgIyB0ZWxsIHRoZSBmdW5jdGlvbiB0aGF0IHdlIHdhbnQgYSB0aWJibGUgb3V0cHV0IChzaW1pbGFyIHRvIGRhdGFmcmFtZSkKICAgICAgICAgeW1pbiA9IHkgLSBzZCh4KSwgIyBjYWxjdWxhdGVzIHRoZSBtaW5pbXVtIHZhbHVlIGZvciBlcnJvciBiYXIKICAgICAgICAgeW1heCA9IHkgKyBzZCh4KSkgIyBjYWxjdWxhdGVzIHRoZSBtYXhpbXVtIHZhbHVlIGZvciBlcnJvciBiYXIKfQpgYGAKCk5vdyB3ZSBjYW4gY3JlYXRlIG91ciBwbG90OgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICB5ID0gUGV0YWwuTGVuZ3RoKSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImNvbCIsIAogICAgICAgICAgICAgICBmdW4uZGF0YSA9IG1lYW4uc2QsIAogICAgICAgICAgICAgICBhZXMoZmlsbCA9IFNwZWNpZXMpLAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImVycm9yYmFyIiwKICAgICAgICAgICAgICAgZnVuLmRhdGEgPSBtZWFuLnNkLAogICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gU3BlY2llcyksCiAgICAgICAgICAgICAgc2hhcGUgPSAxLAogICAgICAgICAgICAgIHdpZHRoID0gMC4yKQpgYGAKCiMjIyMgRmFjZXRzCgpGYWNldGluZyBpcyBhIHRlY2huaXF1ZSB0aGF0IGFsbG93cyB1cyB0byBzZXBhcmF0ZSBkYXRhIG91dCBpbnRvIHBhbmVscyBiYXNlZCBvbiBhIHZhcmlhYmxlIGluIHRoZSBkYXRhZnJhbWUuIFRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpemluZyBjb21wbGV4IGRhdGEgd2hlcmUgaXQgbWF5IGJlIGVhc2llciB0byBzZWUgcGF0dGVybnMgd2hlbiB0aGUgZGF0YSBhcmUgc2VwYXJhdGVkLgoKVGhlcmUgYXJlIHR3byBtZXRob2RzIHRvIGNyZWF0ZSBmYWNldHMgaW4gYSBwbG90OiBgZmFjZXRfd3JhcCgpYCBhbmQgYGZhY2V0X2dyaWQoKWAuIElmIHlvdSBhcmUgb25seSBjcmVhdGluZyBmYWNldHMgYmFzZWQgb24gb25lIHZhcmlhYmxlIChlLmcuIHNwZWNpZXMpIHlvdSBjYW4gdXNlIGBmYWNldF93cmFwKClgIGJ1dCBpZiB5b3UgaGF2ZSBhIG1vcmUgY29tcGxleCBwbG90IHdoZXJlIHlvdSB3YW50IHRvIGNyZWF0ZSBmYWNldHMgYmFzZWQgb24gdHdvIHZhcmlhYmxlcyAoZS5nLiBzcGVjaWVzICphbmQqIHRpbWUgcG9pbnQpIHlvdSBuZWVkIHRvIHVzZSBgZmFjZXRfZ3JpZCgpYC4KCkxldCdzIHB1bGwgdXAgYW5vdGhlciBvZiBgUmAncyBidWlsdC1pbiBkYXRhc2V0cyAobXRjYXJzKSB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gc2VlIGJvdGggb2YgdGhlc2UgaW4gYWN0aW9uLiBtdGNhcnMgaXMgYnVpbHQgZnJvbSBkYXRhIGV4dHJhY3RlZCBmcm9tIHRoZSAxOTc0IE1vdG9yIFRyZW5kIFVTIG1hZ2F6aW5lLCBhbmQgY29tcHJpc2VzIGZ1ZWwgY29uc3VtcHRpb24gYW5kIDEwIGFzcGVjdHMgb2YgYXV0b21vYmlsZSBkZXNpZ24gYW5kIHBlcmZvcm1hbmNlIGZvciAzMiBhdXRvbW9iaWxlcyAoMTk3My0tNzQgbW9kZWxzKS4KCmBgYHtyfQpoZWFkKG10Y2FycykKYGBgCgpMZXQncyBsb29rIGF0IG1wZyAoTWlsZXMgcGVyIFVTIEdhbGxvbikgcGxvdHRlZCBhZ2FpbnN0IGhwIChHcm9zcyBob3JzZXBvd2VyKS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywKICAgICAgIGFlcyh4ID0gaHAsCiAgICAgICAgICAgeSA9IG1wZywKICAgICAgICAgICBjb2xvciA9IG1wZykpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKQpgYGAKCk5vdyBsZXQncyB1c2UgYGZhY2V0X3dyYXAoKWAgdG8gc3BsaXQgdGhlc2UgZGF0YSB1cCBieSB2cyAoRW5naW5lIHNoYXBlLCAwID0gViwgMSA9IHN0cmFpZ2h0KS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywKICAgICAgIGFlcyh4ID0gaHAsCiAgICAgICAgICAgeSA9IG1wZywKICAgICAgICAgICBjb2xvciA9IG1wZykpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZmFjZXRfd3JhcCh+IHZzKQpgYGAKCkxldCdzIGFkZCBhbm90aGVyIHZhcmlhYmxlIGZhY2V0IHdpdGggYGZhY2V0X2dyaWQoKWAgYW5kIHNwbGl0IHRoZSBkYXRhIGJ5IGFtIChUcmFuc21pc3Npb24sIDAgPSBhdXRvbWF0aWMsIDEgPSBtYW51YWwpIGFzIHdlbGwuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtdGNhcnMsCiAgICAgICBhZXMoeCA9IGhwLAogICAgICAgICAgIHkgPSBtcGcsCiAgICAgICAgICAgY29sb3IgPSBtcGcpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIGZhY2V0X2dyaWQoY29scyA9IHZhcnModnMpLCAjIGFzc2lnbiBhIHZhcmlhYmxlIHRvIHRoZSBjb2x1bW4gcGFuZWxzCiAgICAgICAgICAgICByb3dzID0gdmFycyhhbSkpICMgYXNzaWduIGEgdmFyaWFibGUgdG8gdGhlIHJvdyBwYW5lbHMKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIGRpZmZlcmVudCBjb3JyZWxhdGlvbnMgYmV0d2VlbiBocCBhbmQgbXBnIGRlcGVuZGluZyBvbiB0aGUgb3RoZXIgcXVhbGl0aWVzIG9mIHRoZSBjYXIuIEhvd2V2ZXIsIHRoaXMgcGxvdCBpcyBub3cgZGlmZmljdWx0IHRvIHJlYWQgYmVjYXVzZSBib3RoIHZhcmlhYmxlcyBhcmUgYmluYXJpZXMsIG1lYW5pbmcgaXQncyBoYXJkIHRvIHRlbGwgd2hhdCdzIHdoYXQuIExldCdzIHRpZHkgdXAgdGhlc2UgcGxvdHMgYW5kIGFkZCBzb21lIGxhYmVscy4KCkNoYW5naW5nIHRoZSBwYW5lbCBsYWJlbHMgd2l0aG91dCBjaGFuZ2luZyB0aGUgdW5kZXJseWluZyBkYXRhIGlzIHNsaWdodGx5IG1vcmUgY29tcGxleCB0aGFuIGNoYW5naW5nIGF4aXMgdGl0bGVzLCBzbyBsZXQncyBsb29rIGF0IGhvdyB0byBkbyB0aGF0LgoKYGBge3J9CnZzTGFicyA8LSBjKCIwIiA9ICJWLXNoYXBlZCIsCiAgICAgICAgICAgICIxIiA9ICJTdHJhaWdodCIpICMgY3JlYXRlIGEgdmVjdG9yIHRoYXQgbWF0Y2hlcyB0aGUgYmluYXJ5IHZhcmlhYmxlcyB0byB0aGVpciB2YWx1ZXMKCmFtTGFicyA8LSBjKCIwIiA9ICJBdXRvbWF0aWMiLAogICAgICAgICAgICAiMSIgPSAiTWFudWFsIikgIyBkbyB0aGUgc2FtZSBmb3IgdGhlIGFtIHZhcmlhYmxlCgpnZ3Bsb3QoZGF0YSA9IG10Y2FycywKICAgICAgIGFlcyh4ID0gaHAsCiAgICAgICAgICAgeSA9IG1wZywKICAgICAgICAgICBjb2xvciA9IG1wZykpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyh2cyksCiAgICAgICAgICAgICByb3dzID0gdmFycyhhbSksCiAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsbGVyKC5jb2xzID0gdnNMYWJzLCAjIHVzZSB0aGUgbGFiZWxsZXIgZnVuY3Rpb24gdG8gYXNzaWduIHRoZXNlIGxhYmVscyB0byB0aGUgcm93cyBhbmQgY29sdW1ucyBvZiB0aGUgcGxvdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAucm93cyA9IGFtTGFicykpIC0+IGZhY2V0UGxvdAoKZmFjZXRQbG90CmBgYAoKTGV0J3MgdGlkeSB0aGUgcmVzdCBvZiB0aGlzIHBsb3QgdXAgYW5kIHRoZW4gc2F2ZSBpdCB0byBmaWxlLgoKYGBge3J9CmZhY2V0UGxvdCArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobmFtZSA9ICJNaWxlcyBwZXJcblVTIEdhbGxvbiIsICMgXG4gc3RhcnRzIGEgbmV3IGxpbmUgaW4gdGhlIGxlZ2VuZCB0aXRsZQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAicHVycGxlIiwKICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAib3JhbmdlIikgKyAjIGNoYW5nZSBjb2xvciBvZiBzY2FsZQogIHRoZW1lX2J3KCkgKwogIHhsYWIoIkdyb3NzIGhvcnNlcG93ZXIiKSArICMgYWRkIHggYXhpcyB0aXRsZQogIHlsYWIoIk1pbGVzIHBlciBVUyBHYWxsb24iKSArICMgYWRkIHkgYXhpcyB0aXRsZQogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpKSAtPiBmYWNldFBsb3RUaWR5ICMgcmVtb3ZlIGdyZXkgYmFja2dyb3VuZCBmcm9tIHBhbmVsIHRpdGxlcwoKZmFjZXRQbG90VGlkeQpgYGAKCk5vdyB3ZSBjYW4gdXNlIHRoZSBzYW1lIG1ldGhvZHMgYXMgZWFybGllciB0byBzYXZlIG91ciBwbG90IHRvIGVpdGhlciBhIFBERiBvciBpbWFnZSBmaWxlIChvciBib3RoISkuCgpgYGB7cn0KcGRmKCJtdWx0aS5mYWNldC5tdGNhcnMucGxvdC5wZGYiLCAjIGZpbGUgbmFtZSB0byB3cml0ZSB0bwogICAgaGVpZ2h0ID0gNCwgIyBwbG90IGhlaWdodCBpbiBpbmNoZXMKICAgIHdpZHRoID0gNikgIyBwbG90IHdpZHRoIGluIGluY2hlcwoKZmFjZXRQbG90VGlkeSAjIHRlbGwgUiB3aGljaCBwbG90IHRvIHdyaXRlIHRvIGZpbGUKCmRldi5vZmYoKSAjIHRoaXMgdGVsbHMgUiB0aGF0IHlvdSdyZSBkb25lIGNyZWF0aW5nIGEgZmlsZQpgYGAKCmBgYHtyfQpnZ3NhdmUocGxvdCA9IGZhY2V0UGxvdFRpZHksICMgc3BlY2lmeSBwbG90CiAgICAgICAibXVsdGkuZmFjZXQubXRjYXJzLnBsb3QudGlmZiIsICMgc3BlY2lmeSBmaWxlIG5hbWUKICAgICAgIGhlaWdodCA9IDQsICMgcGxvdCBoZWlnaHQKICAgICAgIHdpZHRoID0gNiwgIyBwbG90IHdpZHRoCiAgICAgICB1bml0cyA9IGMoImluIiksICMgc3BlY2lmeSB3aGljaCB1bml0cyB0byB1c2UgZm9yIGhlaWdodCBhbmQgd2lkdGgKICAgICAgIGRldmljZSA9ICJ0aWZmIikgIyBzcGVjaWZ5IGZpbGUgdHlwZSBmb3Igc2F2aW5nIC0gZ2dzYXZlIHdpbGwgYWxzbyBndWVzcyBkZXBlbmRpbmcgb24gdGhlIGV4dGVuc2lvbiB1c2VkIGluIGZpbGUgbmFtZQpgYGAKCiMjIyMgUGxvdHRpbmcgYSB0aW1lIGNvdXJzZQoKRm9yIG1hbnkgZXhwZXJpbWVudHMsIGl0J3MgaW1wb3J0YW50IHRvIGJlIGFibGUgdG8gcGxvdCBhIHRpbWUgY291cnNlLiBMZXQncyBsb2FkIGluIHNvbWUgZXhhbXBsZSBjb2xvbnkgY291bnQgZGF0YSBmcm9tIGFuIGV4cGVyaW1lbnQgZ3Jvd2luZyBmb3VyIHNwZWNpZXMgb2YgYmFjdGVyaWEgaW4gYm90aCBoaWdoIGFuZCBsb3cgaXJvbiBjb25kaXRpb25zLCB3aXRoIHRpbWUgcG9pbnRzIGF0IDAsIDYsIGFuZCAyNCBob3Vycy4KCmBgYHtyfQpyZWFkLmNzdigiY2Z1X2NvdW50c19yYXcuY3N2IikgLT4gY291bnRzICMgcmVhZCBpbiBjb3VudHMKYGBgCgpMZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgZm9ybWF0IG9mIHRoZSBkYXRhIHdlIGp1c3QgbG9hZGVkIGFuZCBjaGVjayB0aGF0IHRoZSBmb3JtYXQgbG9va3MgY29ycmVjdCBmb3IgcGxvdHRpbmcuCgpgYGB7cn0KaGVhZChjb3VudHMpCmBgYAoKT3VyIGRhdGFmcmFtZSBoYXMgY29sdW1ucyBhcyAqKnZhcmlhYmxlcyoqIGFuZCByb3dzIGFzICoqb2JzZXJ2YXRpb25zKiogc28gd2UncmUgZ29vZCB0byBnbyEKCkluIG9yZGVyIHRvIHBsb3QgYSB0aW1lIGNvdXJzZSBhcyBhIGRpc2NyZXRlIHZhcmlhYmxlIHRoYXQgcnVucyBhbG9uZyB0aGUgeCBheGlzLCB3ZSBuZWVkIHRvIGNoYW5nZSB0aGUgYHRpbWVgIHZhcmlhYmxlIGZyb20gbnVtZXJpYyB0byBhIGZhY3RvciBpbiBib3RoIHRoZSByYXcgY291bnRzIGFuZCBncm91cCBtZWFucyBkYXRhZnJhbWVzLiBGYWN0b3JzIGNhbiBoZWxwIHVzIGNvbnRyb2wgdGhlIG9yZGVyIGluIHdoaWNoIG9ic2VydmF0aW9ucyBhcmUgcGxvdHRlZC4gQnkgZGVmYXVsdCwgZ2dwbG90IHdpbGwgcGxvdCBudW1lcmljIHZhcmlhYmxlcyBpbiBhc2NlbmRpbmcgb3JkZXIgYW5kIGNoYXJhY3RlciBvciBmYWN0b3IgdmFyaWFibGVzIGluIGFscGhhYmV0aWNhbCBvcmRlci4gU28sIHdlJ2xsIGFsc28gc2V0IHRoZSBpcm9uIGxldmVsIGFzIGEgZmFjdG9yIGJlY2F1c2UgSSB3YW50IHRvIHBsb3QgdGhlIGxvdyBpcm9uIGNvbmRpdGlvbiAqYmVmb3JlKiB0aGUgaGlnaCBpcm9uIGNvbmRpdGlvbi4KCmBgYHtyfQpjb3VudHMkdGltZSA8LSBmYWN0b3IoY291bnRzJHRpbWUpCgpjb3VudHMkaXJvbiA8LSBmYWN0b3IoY291bnRzJGlyb24sCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJMb3cgaXJvbiIsIkhpZ2ggaXJvbiIpKQpgYGAKCk5vdyB3ZSBjYW4gc2V0IG91ciBjdXN0b20gY29sb3JzIGZvciB0aGUgcGxvdC4KCmBgYHtyfQpzcGVjaWVzQ29scyA8LSBjKCJQc2V1ZG9tb25hcyBhZXJ1Z2lub3NhIiA9ICIjNDNiYThmIiwKICAgICAgICAgICAgIlN0YXBoeWxvY29jY3VzIGF1cmV1cyIgPSAiI2ZlYzQ0ZiIsCiAgICAgICAgICAgICJTdHJlcHRvY29jY3VzIHNhbmd1aW5pcyIgPSAiIzQyOTJjNiIsCiAgICAgICAgICAgICJCdXJraG9sZGVyaWEgb3JiaWNvbGEiID0gIiNkNTdiZDQiKQpgYGAKCkxldCdzIGNyZWF0ZSBhIGxpbmUgcGxvdCBvZiBsb2cxMCBDRlUvbUwgcGVyIHNwZWNpZXMgb3ZlciB0aW1lLCB3aXRoIGZhY2V0cyBzaG93aW5nIHRoZSBoaWdoIGFuZCBsb3cgaXJvbi4gV2Ugd2lsbCBwbG90IGEgcmliYm9uIHRoYXQgcmVwcmVzZW50cyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIChgc2RgKSwgdGhpbiBsaW5lcyB0aGF0IHJlcHJlc2VudCBlYWNoIHJlcGxpY2F0ZSAoYHRlY2gucmVwYCksIGFuZCBhIHRoaWNrIGxpbmUgcmVwcmVzZW50aW5nIHRoZSBtZWFuIENGVS9tTCBmb3IgZWFjaCBzcGVjaWVzIChgbWVhbi5jZnVgKS4gV2UnbGwgdXRpbGl6ZSB0aGUgYHN0YXRfc3VtbWFyeWAgZnVuY3Rpb24gdGhhdCB3ZSBzYXcgZWFybGllci4KCmBgYHtyfQpjb3VudHMgJT4lCiAgbXV0YXRlKGxvZzEwLmNmdSA9IGxvZzEwKGNmdSkpICU+JSAjIGNyZWF0ZSBhIG5ldyBjb2x1bW4gd2l0aCBsb2cxMCBDRlUgdmFsdWVzCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwKICAgICAgICAgICAgIHkgPSBsb2cxMC5jZnUpKSArCiAgc3RhdF9zdW1tYXJ5KGdlb20gPSAicmliYm9uIiwKICAgICAgICAgICAgICAgZnVuLmRhdGEgPSBtZWFuLnNkLAogICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBzcGVjaWVzLCAjIGdyb3VwIGFlc3RoZXRpYyBzcGVjaWZpZXMgaG93IHRoZSBsaW5lcyBhcmUgam9pbmVkIHRvZ2V0aGVyCiAgICAgICAgICAgICAgICAgICBmaWxsID0gc3BlY2llcyksCiAgICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGludGVyYWN0aW9uKHNwZWNpZXMsaXJvbix0ZWNoLnJlcCksCiAgICAgICAgICAgICAgICBjb2xvciA9IHNwZWNpZXMpLAogICAgICAgICAgICBsaW5ld2lkdGggPSAwLjEsCiAgICAgICAgICAgIGFscGhhID0gMC43KSArCiAgc3RhdF9zdW1tYXJ5KGdlb20gPSAibGluZSIsCiAgICAgICAgICAgICAgIGZ1bi5kYXRhID0gbWVhbi5zZCwKICAgICAgICAgICAgICAgYWVzKGNvbG9yID0gc3BlY2llcywKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gaW50ZXJhY3Rpb24oc3BlY2llcyxpcm9uKSksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJTcGVjaWVzIiwgIyBib3RoIGNvbG9yIGFuZCBmaWxsIG11c3QgaGF2ZSB0aGUgc2FtZSBuYW1lIGlmIHdlIHdhbnQgdG8gY29tYmluZSB0aGUgbGVnZW5kCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IHNwZWNpZXNDb2xzKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJTcGVjaWVzIiwKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBzcGVjaWVzQ29scykgKwogIGxhYnMoeCA9ICJUaW1lIChoKSIsICMgc2V0IGF4aXMgbGFiZWxzCiAgICAgICB5ID0gIkxvZzxzdWI+MTA8L3N1Yj4gQ0ZVL21MIikgKwogIHRoZW1lX2J3KCkgKyAjIHJlbW92ZSBncmV5IGJhY2tncm91bmQKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKGlyb24pKSArICMgZmFjZXQgcGxvdCBieSBoaWdoL2xvdyBpcm9uCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgIyByZW1vdmUgZ3JleSBiYWNrZ3JvdW5kIGZyb20gZmFjZXQgdGl0bGVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIiksICMgc2V0IGxlZ2VuZCBmb250IHRvIGl0YWxpYwogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJpbnNpZGUiLCAjIG1vdmUgbGVnZW5kIGluc2lkZSBib3VuZHMgb2YgcGxvdAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbi5pbnNpZGUgPSBjKDAuOCwwLjIpLCAjIHVzZSBhIHZlY3RvciB0byBzZXQgeCBhbmQgeSBwb3NpdGlvbiAoMCAtIDEpCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsICMgc2V0IGJveCBhcm91bmQgbGVnZW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICAsCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9tYXJrZG93bigpKSAjIGFsbG93IGF4aXMgdGl0bGUgdG8gcmVhZCBodG1sIGNvZGUgZm9yIHN1YnNjcmlwdApgYGAKCiMjIyBBY3Rpdml0aWVzCgojIyMjIEdyZWVuIDEKCkNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgY29sdW1ucyBTZXBhbC5MZW5ndGggKHgpIGFuZCBTZXBhbC5XaWR0aCAoeSkgZnJvbSB0aGUgaXJpcyBkYXRhc2V0LgoKYGBge3J9CgpgYGAKCiMjIyMgR3JlZW4gMgoKTWFrZSBhIHBsb3Qgd2hlcmUgYWxsIHRoZSBwb2ludHMgYXJlIGdyZWVuLCBhbmQgdGhlIGxpbmUgaXMgY29sb3JlZCBieSB0aGUgc3BlY2llcyBvZiBpcmlzLgoKYGBge3J9CgpgYGAKCiMjIyMgQmx1ZSAxCgpNYWtlIGEgcGxvdCB0aGF0IGluY2x1ZGVzIHJlZ3Jlc3Npb24gbGluZXMgZm9yIGluZGl2aWR1YWwgc3BlY2llcyBhcyB3ZWxsIGFzIHRoZSBvdmVyYWxsIGRhdGEuCgpgYGB7cn0KCmBgYAoKCgpgYGB7cn0KaXJpcyAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IDE6NCwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAibWVhc3VyZW1lbnQiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibW0iKSAlPiUKICBncm91cF9ieShTcGVjaWVzLG1lYXN1cmVtZW50KSAlPiUKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4obW0pKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtZWFzdXJlbWVudCwKICAgICAgICAgICAgIHkgPSBtZWFuLAogICAgICAgICAgICAgZmlsbCA9IFNwZWNpZXMpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCg==